package com.wg.core.weight;

import cn.hutool.core.lang.WeightRandom;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
 * 权重管理器（基于配置字符串动态加载权重项并实现随机选择）
 * <p>
 * 职责：
 * - 解析配置字符串并实例化权重项（支持自定义分隔符）
 * - 构建权重随机选择器（WeightRandom）
 * - 提供随机选择权重项的接口（next()）
 *
 * @author 少爷123
 */
public class WeightMgr<T extends BaseWeightItem<T>> {
    private static final Log log = LogFactory.get();

    /** 权重随机选择器（不可变） */
    private final WeightRandom<T> weightRandom;
    /** 权重项列表（不可变） */
    private final List<T> weightItems;

    /**
     * 构造权重管理器（从配置字符串加载权重项）
     *
     * @param clazz         权重项类（需继承 BaseWeightItem 且包含无参构造函数）
     * @param configStr     配置字符串（格式示例："item1|10;item2|20"）
     * @param lineSeparator 行分隔符（用于分割多个权重项，如 ";"）
     * @param partSeparator 部分分隔符（用于分割权重项的键值对，如 "|"）
     * @throws IllegalArgumentException 若配置字符串格式无效或实例化失败
     */
    public WeightMgr(Class<T> clazz, String configStr,
            CharSequence lineSeparator, CharSequence partSeparator) {
        validateBaseParams(clazz, configStr, lineSeparator, partSeparator);

        List<T> loadedItems = loadWeightItems(clazz, configStr, lineSeparator, partSeparator);

        // 3. 构建权重随机选择器
        this.weightItems = Collections.unmodifiableList(loadedItems);
        this.weightRandom = buildWeightRandom(loadedItems);
    }

    /**
     * 随机选择一个权重项（基于权重值概率）
     *
     * @return 随机选中的权重项（不会返回 null）
     * @throws IllegalStateException 若权重项列表为空
     */
    public T next() {
        if (weightItems.isEmpty()) {
            throw new IllegalStateException("权重项列表为空，无法选择");
        }
        return weightRandom.next();
    }

    // ------------------------------ 私有工具方法 ------------------------------
    /**
     * 校验构造函数基础参数
     */
    private void validateBaseParams(Class<T> clazz, String configStr,
            CharSequence lineSeparator, CharSequence partSeparator) {
        // 校验类对象非空
        Objects.requireNonNull(clazz, "权重项类不能为 null");
        // 校验配置字符串非空
        if (StrUtil.isBlank(configStr)) {
            throw new IllegalArgumentException("配置字符串不能为 null 或空");
        }
        // 校验分隔符非空
        if (StrUtil.isBlank(lineSeparator) || StrUtil.isBlank(partSeparator)) {
            throw new IllegalArgumentException("分隔符不能为 null 或空");
        }
    }

    /**
     * 从配置字符串加载权重项列表
     */
    private List<T> loadWeightItems(Class<T> clazz, String configStr,
            CharSequence lineSeparator, CharSequence partSeparator) {
        List<T> items = new ArrayList<>();
        String[] configLines = StrUtil.splitToArray(configStr, lineSeparator);

        for (int i = 0; i < configLines.length; i++) {
            String line = StrUtil.trim(configLines[i]);
            if (StrUtil.isBlank(line)) {
                log.warn("第 {} 行配置为空，跳过", i + 1);
                continue;
            }

            String[] parts = StrUtil.splitToArray(line, partSeparator);
            if (parts.length < 2) {
                log.error("第 {} 行配置格式无效（至少需要2个部分），内容：{}", i + 1, line);
                throw new IllegalArgumentException("配置行格式错误：'" + line + "'");
            }

            try {
                T item = ReflectUtil.newInstance(clazz);
                item.load(parts);
                items.add(item);
            } catch (Exception e) {
                log.error("加载第 {} 行配置失败，内容：{}", i + 1, line, e);
                throw new IllegalArgumentException("加载配置失败：'" + line + "'", e);
            }
        }

        if (items.isEmpty()) {
            throw new IllegalArgumentException("配置字符串中无有效权重项");
        }
        return Collections.unmodifiableList(items);
    }

    /**
     * 构建权重随机选择器（WeightRandom）
     */
    private WeightRandom<T> buildWeightRandom(List<T> items) {
        List<WeightRandom.WeightObj<T>> weightObjs = new ArrayList<>();
        for (T item : items) {
            weightObjs.add(new WeightRandom.WeightObj<>(item, item.getWeight()));
        }
        return RandomUtil.weightRandom(weightObjs);
    }
}